#ifndef APP
#define APP

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <exception>

#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "Locker.hpp" // 互斥锁、条件变量、信号量，的实现
#include "ThreadPool.hpp" // 线程池的实现
#include "HttpConnection.hpp" // 实现http连接

using namespace std;

constexpr int MAX_FILE_DESCRIPTOR = 10000; // 最多的文件描述符个数
constexpr int MAX_EPOLL_EVENTS = 5000; // epoll最多同时监听的事件的个数

// 定义一个用来处理信号的函数
void register_signal_handler(int signal, void(*handler)(int)) {
    struct sigaction signal_action; // 定义一个信号的处理器
    memset(&signal_action, 0, sizeof(signal_action)); // 先清空
    signal_action.sa_handler = handler; // 设置handler
    
    int res;
    res = sigfillset(&signal_action.sa_mask); // 设置一个包含了所有信号的信号集，即阻塞所有的信号
    if (res == -1) {
        cerr << "Sginal Fill Set Fail, errno = " << errno << endl;
        perror("Sginal Fill Set Fail, ");
        exit(-1);
    }

    res = sigaction(signal, &signal_action, nullptr); // 注册信号处理
    if (res == -1) {
        cerr << "Signal Action Fail, errno = " << errno << endl;
        perror("Signal Action Fail, ");
        exit(-1);
    }
}

int main(int argc, char *argv[]) {

    // 定义端口号
    int port_num = 2333;

    // 处理SIGPIPE信号，即客户端主动关闭连接的情况，这里采用忽略SIGPIPE信号的策略
    register_signal_handler(SIGPIPE, SIG_IGN);

    // 创建并初始化线程池
    MyThreadPool<HttpConnection> *thread_pool = nullptr;
    try {
        thread_pool = new MyThreadPool<HttpConnection>(10, 1000);
    } catch (exception&) {
        cerr << "Fail to Create Thread Pool, exit now" << endl;
        exit(-1);
    }

    // 创建Socket（TCP）
    int tcp_listen_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (tcp_listen_socket_fd == -1) {
        cerr << "Creating TCP Socket Fail, errno = " << errno << endl;
        perror("Creating TCP Socket Fail, ");
        exit(-1);
    }

    // 设置Socket的端口复用
    int reuse = 1;
    int res = setsockopt(tcp_listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    if (res == -1) {
        cerr << "Socket Set Reuse Failed, errno = " << errno << endl;
        perror("Socket Set Reuse Failed, ");
        exit(-1);
    }

    // 将Socket绑定到指定的IP和端口号上
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(port_num);

    res = bind(tcp_listen_socket_fd, (struct sockaddr*)&address, sizeof(address));
    if (res == -1) {
        cerr << "Socket Bind Fail, errno = " << errno << endl;
        perror("Socket Bind Fail, ");
        exit(-1);
    }

    // 开始监听Socket
    res = listen(tcp_listen_socket_fd, 100);
    if (res == -1) {
        cerr << "Socket Listen Fail, errno = " << errno << endl;
        perror("Socket Listen Fail, ");
        exit(-1);
    }

    // 创建epoll对象，添加监听的文件描述符
    int epoll_fd = epoll_create(100);

    if (epoll_fd == -1) {
        cerr << "Epoll Create Fail, errno = " << errno << endl;
        perror("Epoll Create Fail, ");
        exit(-1);
    }

    // 注册socket上的listen事件，不需要one-shot
    epoll_add_file_descriptor(epoll_fd, tcp_listen_socket_fd, false);
    cout << "Now Listen on Scoket, fd is " << tcp_listen_socket_fd << endl;

    // 传递epoll实例的文件描述符，初始化静态成员
    HttpConnection::shared_epoll_fd = epoll_fd;
    HttpConnection::connected_client = 0;

    // 创建事件数组，用于接收epoll事件
    struct epoll_event epoll_events[MAX_EPOLL_EVENTS];

    // 创建HTTP Connection数组，用于接收来自客户端的
    HttpConnection *clients = new HttpConnection[MAX_FILE_DESCRIPTOR];

    // while循环，主线程不断的监测有没有事件发生
    while (true) {
        // 等待epoll事件，返回接收到的事件的数量
        cout << endl;
        cout << "Waiting Epoll..." << endl;
        cout << "------------------------------------------------------------" << endl;
        int num_events = epoll_wait(epoll_fd, epoll_events, MAX_EPOLL_EVENTS, -1);

        // 调用epoll失败，出现了无法解决的问题（传递参数正确的时候，不会发生其他的错误）
        if (num_events == -1 && errno != EINTR) {
            cerr << "Epoll Wait Fail, errno = " << errno << endl;
            perror("Epoll Wait Fail, ");
            break;
        }

        // 循环遍历事件数组，处理每一个事件
        for (int i = 0; i < num_events; ++i) {
            // 获取事件对应的socket文件描述符
            int epoll_event_fd = epoll_events[i].data.fd;
            cout << "Epoll Event fd is " << epoll_event_fd << endl;

            // 如果事件的文件描述符，和我们监听的socket文件描述符相同，说明是有新的客户端连接进来了
            if (epoll_event_fd == tcp_listen_socket_fd) {
                // 接收客户端的连接
                struct sockaddr_in client_address;
                socklen_t client_address_len = sizeof(client_address);
                int client_fd = accept(tcp_listen_socket_fd, (struct sockaddr*)&client_address, &client_address_len);

                if (client_fd == -1) {
                    cerr << "Accept Client Fail, errno = " << errno << endl;
                    perror("Accept Client Fail, ");
                    continue;
                }

                // 检查客户端连接数，是否超过预设的最大值
                if (HttpConnection::connected_client >= MAX_FILE_DESCRIPTOR) {
                    cout << "Max File Descriptor Reached, Closing Client" << endl; 
                    int res = close(client_fd);

                    if (res != 0) {
                        cerr << "Closing Client Fail, errno = " << errno << endl;
                        perror("Closing Client Fail, ");
                        continue;
                    }
                }

                cout << "Connecting to New Client, fd is " << client_fd << endl;

                // 将客户端发来的HTTP请求信息，存储到上面申请的数组中
                clients[client_fd].init_connection(client_fd, client_address);
            }

            // 处理客户端异常断开，或者出现错误的情况
            else if (epoll_events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                cout << "Error Occurred, Client Lost, Colsing Connection" << endl;
                clients[epoll_event_fd].close_connection();
            }

            // epoll读事件发生
            else if (epoll_events[i].events & EPOLLIN) {
                cout << "Epoll Read Event Occurred" << endl;

                if (clients[epoll_event_fd].read()) {
                    thread_pool->add_request(&clients[epoll_event_fd]);
                } else {
                    clients[epoll_event_fd].close_connection();
                }
            }

            // epoll写事件发生
            else if (epoll_events[i].events & EPOLLOUT) {
                cout << "Epoll Write Event Occurred" << endl;

                if (!clients[epoll_event_fd].write()) {
                    clients[epoll_event_fd].close_connection();
                }
            }

            cout << endl;
        }
    }
    
    // 关闭文件描述符，释放new出来的资源
    close(tcp_listen_socket_fd);
    close(epoll_fd);
    delete thread_pool;
    delete[] clients;
}

#endif
